PostgreSQL上でのパスワード変更時に認証情報をAWS Secrets Managerに連携するようにしてみました

PostgreSQL上でのパスワード変更時に認証情報をAWS Secrets Managerに連携するようにしてみました

Clock Icon2024.08.26

初めに

現在Amazon RDSではAWS Secrets Manager統合機能を利用してSecrets Managerを活用した認証情報の管理が可能となっております。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-secrets-manager.html

こちらを利用することでDBの認証情報でSecrets Managerのパスワードローテーションの恩恵を受けることが可能であり、またアプリ側としてもSecrets Manager上のシークレットを参照していれば認証情報変更の際に特別対応を行う必要がないというのがメリットとなります。

一方でこちらの機能はSecrets Manager主体の管理となるため、アプリ的にSecrets Managerに認証を格納したいけどDB管理者としては今まで通りPostrgreSQL上でSQLを流してやりたいという場合には利用しづらいかもしれません。

今回はそれを実現できるようPostgreSQL側からSecrets Managerにいい感じにパスワードを同期できる様にしてみます。

なお本記事はRDSを利用する形で記載をしておりますが、仕組み上オンプレミス上のPostgreSQL環境などRDS外でも同等のことが実現可能となります(少し変更は必要ですが)。

実装概要

PostgreSQLの拡張機能であるpg_tleのパスワードチェックフックおよびaws_lambdaを利用します。

https://dev.classmethod.jp/articles/password-check-hook-change-limit-used-pg-tle-1-3-0/

こちらを利用してパスワード変更時にlambda関数を呼び出しそこからSecrets Managerに認証情報を登録します。

RDSに割り当てるIAMロールおよびlambda関数はSAMで実装を行い以下に格納をしています。
Lambda関数側の処理としてはほぼシンプルにSecretsを格納しているだけで複雑なことはしていません。

https://github.com/cm-suzuki-junya/pgsql-sync-credential-to-sm

RDSの作成・設定、VPCエンドポイントもしくはNAT Gatewayの作成(プライベートサブネットの場合)は別途手動で行う形を想定しております。

手動設定

上記のSAMによるデプロイとは別にRDS側への設定を行います。

パラメーターグループに以下の値を割り当てます。

パラメータ 備考
shared_preload_library pg_tle 既存パラメータがある場合そこへの追加
pgtle.enable_password_check on
rds.custom_dns_resolution 1 VPCエンドポイントを利用する場合

またRDSがLambda関数を実行できるようにSAMで作成されるIAMロールを割り当てておきます。

上記実行後以下のSQLを実行しパスワード変更時のフック処理をインストールします。

CREATE EXTENSION aws_lambda CASCADE;
CREATE EXTENSION pg_tle;

SELECT pgtle.install_extension(
  'sync_secrets',
  '1.0',
  'Sync credential to Secrets Manager',
$_pgtle_$
  CREATE SCHEMA sync_secrets;

  CREATE FUNCTION sync_secrets.hook_function(username text, password text, password_type pgtle.password_types, valid_until timestamptz, valid_null boolean)
  RETURNS void AS $$
    DECLARE
      context json;
    BEGIN
      context := '{"userName": "'|| hook_function.username ||'", "password": "'||  hook_function.password || '"}';
      PERFORM aws_lambda.invoke(
          'arn:aws:lambda:ap-northeast-1:xxxxxx:function:pgsql-sync-login-user-info-SyncUserFunction-xxxxxx',
          context::json
      );
    END
  $$ LANGUAGE plpgsql;

  -- 上記の関数をパスワード変更時のフック処理として登録
  SELECT pgtle.register_feature('sync_secrets.hook_function', 'passcheck');
  REVOKE ALL ON SCHEMA sync_secrets FROM PUBLIC;
$_pgtle_$,
'{aws_lambda}'
);

上記実行後CREATE EXTENSIONで拡張機能を有効化します。

CREATE EXTENSION sync_secrets;

動作確認

DB側でパスワード変更をフック処理となっておりますので特別な手順は必要なくALTER USERによるパスワードの変更を行うのみでOKです。

postgres=> ALTER USER postgres WITH PASSWORD 'password';
ALTER ROLE

こうすることで先ほどhook_functionに記載したlambda関数の実行処理が呼び出され、その処理を通してSecretsの書き換えが行われます。

changed-secrets

終わりに

パスワードの変更フック処理を活用してDBを起点にSecrets Managerに変更された認証情報を自動で連携するようにしてみました。

DB側の作業は現行通りSQLで行いたい、AWSレイヤーを意識しないでほしいといったようなケースでも利用かつ、アプリ側でSecrets Managerを利用していれば別口での認証情報の引き渡しを不要とし手間を削減することが可能です。

Secrets Managert統合もあくまでシークレットの内容でDB側のパスワードを変えているだけであり、それを無視してDB側でパスワードを変更すると乖離が起きる問題も今回のパスワードフックチェックによる逆方向同期を仕込むことで回避きそうではあります。
(そもそもそれが起きないように制限をかけるべき部分ではありますが...)

一応今回の処理としてはパスワード自体は生でLambdaに引き渡している関係でちょっとしたデバッグでeventの値を吐き出してしまってパスワードがログ上に露出...ということもあり得るため実際に利用する場合はその辺りをどうにかする必要は出てきそうです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.